using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using ApplicationCore;
using System.Data.SqlClient;
using System.ComponentModel;
using System.Linq;

namespace UserSurface
{
    public class MainSurface
    {
        //test
        private DataManager dataMgr;
        private BatchLayout _currenBatchLayoutA;
        private BatchLayout _currenBatchLayoutB;
        private PostProcessor _currentPostProcessor;

        public PostProcessor CurrenPostProcessor
        {
            get { return _currentPostProcessor; }
        }


        //private string currentBatchName;
        //private int currentBatchNumber;
        //private DateTime currentBatchDate;
        private ProcessingInfo _procInfo;


        public SecurityDescriptor SecDescriptor
        {
            get
            {
                return this.dataMgr.SecDesc;
            }
        }

        public ProcessingInfo ProcInfo
        {
            get
            {
                return this._procInfo;
            }
        }

        //for module bactch design
        public BatchLayout CurrenBatchLayoutA
        {
            get { return _currenBatchLayoutA; }
        }

        //for module raw data
        public BatchLayout CurrenBatchLayoutB
        {
            get { return _currenBatchLayoutB; }
        }

        //public EnzymeProcessingDataSet Data
        //{
        //    get
        //    {
        //        return dataMgr.EnzymeDataSet;
        //    }
        //}

        System.Windows.Forms.Form _parent;

        public MainSurface(System.Windows.Forms.Form parent)
        {
            this._parent = parent;
            //  Connect(user, passwd);

            this._procInfo = new ProcessingInfo();
            _procInfo.DataContext = () => { return GetDataContext(); };
        }

        public EnzymeDBDataContext GetDataContext()
        {
            if (_connected)
            {
                return new EnzymeDBDataContext(dataMgr.Connection);
            }
            throw new InvalidOperationException("No database connection established.");
        }

        private EnzymeDBDataContext _currentDataContext;
        public EnzymeDBDataContext CurrentDataContext
        {
            get
            {
                if (_currentDataContext == null)
                {
                    _currentDataContext = GetDataContext();
                }
                return _currentDataContext;
            }
        }

        public void RefreshCurrentFreshweigthView()
        {
            CurrentDataContext.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, CurrentDataContext.FreshWeightViews);
        }

        private bool _connected = false;
        public bool Connect(string user, string passwd)
        {
            try
            {
                this.dataMgr = new DataManager(user, passwd);
                //this.dataMgr.ExportProgress += new EventHandler<ProgressEventArgs>(dataMgr_ExportProgress);
                this._currenBatchLayoutA = new BatchLayout();
                _currenBatchLayoutB = new BatchLayout();
                _currenBatchLayoutA.DataContextAccessor = () => { return GetDataContext(); };
                _currenBatchLayoutB.DataContextAccessor = () => { return GetDataContext(); };
                _procInfo.BatLayout = this._currenBatchLayoutB;
                _connected = true;
                return true;
            }
            catch (SqlException)
            {
                return false;
            }
        }

        void On_ExportProgress(object sender, ProgressEventArgs e)
        {
            if (ExportProgress != null)
                ExportProgress(sender, e);
        }

        public void NavigateBatchA(BatchView bv)
        {
            this._currenBatchLayoutA.LoadLayout(bv);
        }

        public void NavigateBatchB(BatchView bv)
        {
            this._procInfo.Batch = bv;
            this._currenBatchLayoutB.LoadLayout(bv);
        }

        public void NavigateEnzyme(EnzymeView ev)
        {
            this._procInfo.Enzyme = ev;
        }

        public void SaveRawData(ProcessingInfo procInfo, bool proved, string[,] rawData, double?[,] procVals)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                RawData rdr;
                string vel;
                int row;
                int col;
                for (int i = 0; i < rawData.GetLength(0); i++)
                {
                    if (procInfo.LayoutType == "SPLIT")
                    {
                        row = i % 4;

                        if (i > 3)
                            vel = "max";
                        else
                            vel = "blank";
                    }
                    else
                    {
                        vel = "NA";
                        row = i;
                    }
                    for (int j = 0; j < rawData.GetLength(1); j++)
                    {
                        col = j;
                        rdr = context.RawDatas.SingleOrDefault(x =>

                                    x.BatchName_Fk == procInfo.BatchName &&
                                    x.BatchNumber_Fk == procInfo.BatchNumber &&
                                    x.BatchDate_Fk == procInfo.BatchDate &&
                                    x.Row_Fk == row &&
                                    x.Column_Fk == j &&
                                    x.Velocity == vel &&
                                    x.Enzyme_Fk == procInfo.Enzyme.UId);
                        if (rdr == null)
                        {
                            rdr = new RawData();
                            rdr.proved = proved;
                            rdr.BatchName_Fk = procInfo.BatchName;
                            rdr.BatchNumber_Fk = procInfo.BatchNumber;
                            rdr.BatchDate_Fk = procInfo.BatchDate;
                            rdr.Enzyme_Fk = procInfo.Enzyme.UId;
                            rdr.Row_Fk = row;
                            rdr.Column_Fk = col;
                            rdr.Velocity = vel;
                            if (rawData[i, j].StartsWith("#"))
                            {
                                rdr.excluded = true;
                                rdr.RawValue = Convert.ToDouble(rawData[i, j].Replace("#", ""));
                            }
                            else
                            {
                                rdr.excluded = false;
                                rdr.RawValue = Convert.ToDouble(rawData[i, j]);
                            }
                            context.RawDatas.InsertOnSubmit(rdr);
                        }
                        else //if(rdr.RawValue != rawData[i,j] || rdr.proved != proved)
                        {
                            rdr.proved = proved;
                            if (rawData[i, j].StartsWith("#"))
                            {
                                rdr.excluded = true;
                                rdr.RawValue = Convert.ToDouble(rawData[i, j].Replace("#", ""));
                            }
                            else
                            {
                                rdr.excluded = false;
                                rdr.RawValue = Convert.ToDouble(rawData[i, j]);
                            }
                        }

                    }
                }
                context.SubmitChanges();
            }
            //SaveProcessedValues(procInfo, procVals);
        }

        public void SaveProcessedValues(ProcessingInfo procInfo, double?[,] procVals)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                int rowCount = procVals.GetLength(0);
                if (procInfo.LayoutType == "SPLIT")
                    rowCount = (rowCount / 2);
                ProcessedData pdr;

                for (int i = 0; i < rowCount; i++)
                {
                    for (int j = 0; j < procVals.GetLength(1); j++)
                    {
                        pdr = context.ProcessedDatas.SingleOrDefault(x =>
                                    x.BatchName_Fk == procInfo.BatchName &&
                                    x.BatchNumber_Fk == procInfo.BatchNumber &&
                                    x.BatchDate_Fk == procInfo.BatchDate &&
                                    x.Row_Fk == i &&
                                    x.Column_Fk == j &&
                                    x.Enzyme_Fk == procInfo.Enzyme.UId);

                        if (pdr != null)
                            context.ProcessedDatas.DeleteOnSubmit(pdr);
                    }
                }

                context.SubmitChanges();


                for (int i = 0; i < rowCount; i++)
                {
                    for (int j = 0; j < procVals.GetLength(1); j++)
                    {
                        pdr = new ProcessedData();
                        pdr.BatchName_Fk = procInfo.BatchName;
                        pdr.BatchNumber_Fk = procInfo.BatchNumber;
                        pdr.BatchDate_Fk = procInfo.BatchDate;
                        pdr.Enzyme_Fk = procInfo.Enzyme.UId;
                        pdr.Row_Fk = i;
                        pdr.Column_Fk = j;
                        if (procVals[i, j] != null && procVals[i, j].HasValue && !Double.IsNaN((double)procVals[i, j]))
                            pdr.Value = (double)procVals[i, j];
                        context.ProcessedDatas.InsertOnSubmit(pdr);
                    }
                }
                context.SubmitChanges();
            }
        }

        public string[,] LoadRawData(BatchLayout bl, ProcessingInfo procInfo, out bool proved)
        {
            //  return dataMgr.LoadRawData(this.procInfo, out proved);
            using (EnzymeDBDataContext context = GetDataContext())
            {
                proved = false;
                if (bl.IsEmpty)
                    return null;

                string[,] ret = new string[bl.PlateRowCount, bl.PlateColumnCount];

                var data = (from x in context.RawDatas
                            where x.BatchName_Fk == procInfo.BatchName
                            where x.BatchNumber_Fk == procInfo.BatchNumber
                            where x.BatchDate_Fk == procInfo.BatchDate
                            where x.Enzyme_Fk == procInfo.Enzyme.UId
                            select x).ToList();

                if (data.Count == 0)
                    return null;
                else
                {
                    foreach (var rdr in data)
                    {
                        if (procInfo.LayoutType == "SPLIT")
                        {
                            if (rdr.Velocity == "blank")
                            {
                                if (rdr.excluded)
                                    ret[rdr.Row_Fk, rdr.Column_Fk] = "#" + rdr.RawValue.ToString();
                                else
                                    ret[rdr.Row_Fk, rdr.Column_Fk] = rdr.RawValue.ToString();
                            }
                            else if (rdr.Velocity == "max")
                                if (rdr.excluded)
                                    ret[rdr.Row_Fk + bl.LayoutRowCount, rdr.Column_Fk] = "#" + rdr.RawValue.ToString();
                                else
                                    ret[rdr.Row_Fk + bl.LayoutRowCount, rdr.Column_Fk] = rdr.RawValue.ToString();
                            proved = rdr.proved;
                        }
                        else
                        {
                            if (rdr.excluded)
                                ret[rdr.Row_Fk, rdr.Column_Fk] = "#" + rdr.RawValue.ToString();
                            else
                                ret[rdr.Row_Fk, rdr.Column_Fk] = rdr.RawValue.ToString();
                            proved = rdr.proved;
                        }
                    }
                    return ret;
                }
            }
        }

        public void NavigateExportExperiment(ExperimentView exRow, BackgroundWorker bw)
        {
            this._currentPostProcessor = new PostProcessor();
            this._currentPostProcessor.LoadData(exRow.Id, this.GetDataContext(), bw);
            //this.currentPostProcessor.Progress += new EventHandler<ProgressEventArgs>(On_ExportProgress);
            //return dataMgr.LoadProcessedValues2(exRow.Id, opt);
        }

        public DataTable LoadExportData(PostProcessorOption option, int decimals, BackgroundWorker bw)
        {
            if (this._currentPostProcessor != null)
                return _currentPostProcessor.ApplyExportOption(option, decimals, bw);
            else
                return null;
        }

        public double? BatchBlankAvg()
        {
            return dataMgr.BatchBlankAvg(_procInfo.Material.UId, _procInfo.Enzyme.UId);
        }

        public double? BatchMaxAvg()
        {
            return dataMgr.BatchMaxAvg(_procInfo.Material.UId, _procInfo.Enzyme.UId);
        }

        public double? BatchAvg()
        {
            return dataMgr.BatchAvg(_procInfo.Material.UId, _procInfo.Enzyme.UId);
        }

        public event EventHandler<ProgressEventArgs> ExportProgress;


        //public bool SetFreshWeight(string exp, int sample, int aliquot, double weigth)
        //{
        //    return this.dataMgr.InsertFreshWeight(exp, sample, aliquot, weigth);
        //}

        public void DeleteData()
        {
            dataMgr.DeleteData(this._procInfo);
        }

        //public void Save()
        //{
        //    dataMgr.Save();
        //}

        //public void ReloadFreshWeights()
        //{

        //    this.dataMgr.ReFillFreshWeights();

        //}

        //public void FillAssayQualityControl()
        //{
        //    this.dataMgr.FillAssayQualityControl();
        //}

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        public Guid AddMaterial(string name)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Material m = new Material { Name = name.Trim() };
                context.Materials.InsertOnSubmit(m);
                context.SubmitChanges();
                return m.UId;
            }
        }

        public void UpdateMaterial(Guid uid, string name)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Material m = context.Materials.Single(x => x.UId == uid);
                m.Name = name.Trim();
                context.SubmitChanges();
            }
        }

        public void DeleteMaterial(Guid uid)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Material m = context.Materials.Single(x => x.UId == uid);
                context.Materials.DeleteOnSubmit(m);
                context.SubmitChanges();
            }
        }

        public Guid AddAnalyte(string analyte, string code, string type, double slope)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                EnzymeCode e = new EnzymeCode { UId = Guid.NewGuid(), Analyte = analyte.Trim(), Code = code.Trim(), Type = type, SlopeRatio = slope };
                context.EnzymeCodes.InsertOnSubmit(e);
                context.SubmitChanges();
                return e.UId;
            }
        }

        public void UpdateAnalyte(Guid uid, string enzyme, string code, double slope)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                EnzymeCode m = context.EnzymeCodes.Single(x => x.UId == uid);
                m.Analyte = enzyme.Trim();
                m.Code = code.Trim();
                m.SlopeRatio = slope;
                context.SubmitChanges();
            }
        }

        public void DeleteAnalyte(Guid uid)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                EnzymeCode m = context.EnzymeCodes.Single(x => x.UId == uid);
                context.EnzymeCodes.DeleteOnSubmit(m);
                context.SubmitChanges();
            }
        }

        public void AddActivity(Guid protUid, Guid matUid, Guid enzUid)
        {
            try
            {
                using (EnzymeDBDataContext context = GetDataContext())
                {
                    Activity act = new Activity { Protocol_Fk = protUid, Material_Fk = matUid, EnzymeCode_Fk = enzUid, Value = 1 };
                    context.Activities.InsertOnSubmit(act);
                    context.SubmitChanges();
                }
            }
            catch (Exception e)
            {
                Microsoft.SqlServer.MessageBox.ExceptionMessageBox box = new Microsoft.SqlServer.MessageBox.ExceptionMessageBox(e);
                box.Show(_parent);
            }
        }

        public void UpdateActivity(Guid protUid, Guid matUid, Guid enzUid, double? val, string unit)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Activity act = context.Activities.Single(x =>
                    x.Protocol_Fk == protUid
                    && x.Material_Fk == matUid
                    && x.EnzymeCode_Fk == enzUid);


                Unit u = null;
                if (!string.IsNullOrEmpty(unit))
                    u = context.Units.SingleOrDefault(x => x.Name.ToLower().Trim() == unit.Trim().ToLower());

                act.Value = val;
                act.Unit_Fk = u != null ? u.UId : default(Guid?);
                context.SubmitChanges();
            }
        }

        public void DeleteActivity(Guid protUid, Guid matUid, Guid enzUid)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Activity act = context.Activities.Single(x =>
                    x.Protocol_Fk == protUid
                    && x.Material_Fk == matUid
                    && x.EnzymeCode_Fk == enzUid);
                context.Activities.DeleteOnSubmit(act);
                context.SubmitChanges();
            }
        }

        public Guid AddProtocol(string name, Guid? enzymeUid)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Protocol m = new Protocol { Name = name.Trim(), Enzyme_Fk = enzymeUid };
                context.Protocols.InsertOnSubmit(m);
                context.SubmitChanges();
                return m.UId;
            }
        }

        public void UpdateProtocol(Guid uid, string name, string desc)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Protocol m = context.Protocols.Single(x => x.UId == uid);
                m.Name = name.Trim();
                m.Description = desc;
                context.SubmitChanges();
            }
        }

        public void DeleteProtocol(Guid uid)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Protocol m = context.Protocols.Single(x => x.UId == uid);
                context.Protocols.DeleteOnSubmit(m);
                context.SubmitChanges();
            }
        }

        public void AddSetting(Guid protUid, Guid paramUid, string val, string splitVal)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                ProtocolSetting ps = new ProtocolSetting { Protocol_Fk = protUid, Parameter_Fk = paramUid, Value = val, SplitValue = splitVal };
                context.ProtocolSettings.InsertOnSubmit(ps);
                context.SubmitChanges();
            }
        }

        public void UpdateSetting(Guid protUid, Guid paramUid, string val, string splitVal)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                ProtocolSetting ps = context.ProtocolSettings.Single(x => x.Protocol_Fk == protUid && x.Parameter_Fk == paramUid);
                ps.Value = val;
                ps.SplitValue = splitVal;
                context.SubmitChanges();
            }
        }

        public void DeleteSetting(Guid protUid, Guid paramUid)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                ProtocolSetting ps = context.ProtocolSettings.Single(x => x.Protocol_Fk == protUid && x.Parameter_Fk == paramUid);
                context.ProtocolSettings.DeleteOnSubmit(ps);
                context.SubmitChanges();
            }
        }

        public Guid AddParameter(string name, string datatype, bool isSplitVar)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Parameter m = new Parameter { Name = name.Trim(), Datatype = datatype, IsSplitVariable = isSplitVar };
                context.Parameters.InsertOnSubmit(m);
                context.SubmitChanges();
                return m.UId;
            }
        }

        public void UpdateParameter(Guid uid, string name, string datatype, string unit, bool isSplitVar, string desc)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Parameter m = context.Parameters.Single(x => x.UId == uid);
                m.Name = name.Trim();
                m.Datatype = datatype;
                m.Unit = unit == null ? unit : unit.Trim();
                m.IsSplitVariable = isSplitVar;
                m.Description = desc;
                context.SubmitChanges();
            }
        }

        public void DeleteParameter(Guid uid)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Parameter m = context.Parameters.Single(x => x.UId == uid);
                context.Parameters.DeleteOnSubmit(m);
                context.SubmitChanges();
            }
        }

        public Guid AddExperiment(string id, Guid? materialUId)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Experiment m = new Experiment { UId = Guid.NewGuid(), Id = id.Trim(), Material_Fk = MainSurface.EmptyGuidToNull(materialUId) };
                context.Experiments.InsertOnSubmit(m);
                context.SubmitChanges();
                return m.UId;
            }
        }

        public void UpdateExperiment(Guid uid, string id, Guid? materialUid)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Experiment m = context.Experiments.Single(x => x.UId == uid);
                m.Id = id.Trim();
                m.Material_Fk = MainSurface.EmptyGuidToNull(materialUid);

                context.SubmitChanges();
            }
        }

        private static Guid? EmptyGuidToNull(Guid? guid)
        {
            if (guid.HasValue && guid.Value == Guid.Empty)
                return default(Guid?);
            else
                return guid;
        }

        public void DeleteExperiment(Guid uid)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Experiment m = context.Experiments.Single(x => x.UId == uid);
                context.Experiments.DeleteOnSubmit(m);
                context.SubmitChanges();
            }
        }

        public Guid AddFreshweight(string experimentId, int sample, int aliquot, double? weight, string location = null)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                FreshWeight m = new FreshWeight();
                m.UId = Guid.NewGuid();
                m.Experiment_Fk = experimentId;
                m.Sample = sample;
                m.Aliquot = aliquot;
                m.Value = weight;
                m.Location = location;
                context.FreshWeights.InsertOnSubmit(m);
                context.SubmitChanges();
                return m.UId;
            }

        }

        public void UpdateFreshweight(Guid uid, string experimentId, int sample, int aliquot, double? weight, string location = null)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                FreshWeight m = context.FreshWeights.Single(x => x.UId == uid);
                m.Experiment_Fk = experimentId;
                m.Sample = sample;
                m.Aliquot = aliquot;
                m.Value = weight;
                m.Location = location;
                context.SubmitChanges();
            }
        }

        public void AddOrUpdateFreshweight(string experimentId, int sample, int aliquot, double? weight, string location = null)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                FreshWeight fw = context.FreshWeights.SingleOrDefault(x => x.Experiment_Fk == experimentId
                                                                && x.Sample == sample
                                                                && x.Aliquot == aliquot
                                                                );

                if (fw == null)
                    AddFreshweight(experimentId, sample, aliquot, weight, location);
                else
                    UpdateFreshweight(fw.UId, experimentId, sample, aliquot, weight, location);
            }
        }

        public void DeleteFreshweight(Guid uid)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                FreshWeight m = context.FreshWeights.Single(x => x.UId == uid);
                context.FreshWeights.DeleteOnSubmit(m);
                context.SubmitChanges();
            }
        }


        public Guid AddBatch(string name, int number, DateTime date, string protocol, string layoutType, int rows, int columns)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                var p = context.Protocols.SingleOrDefault(x => x.Name == protocol.Trim());
                Batch b = new Batch();
                b.UId = Guid.NewGuid();
                b.Name = name;
                b.Number = number;
                b.Date = date;
                b.Protocol_Fk = p != null ? p.UId : Guid.Empty;
                b.LayoutType = layoutType;
                b.RowCount = rows;
                b.ColumnCount = columns;
                context.Batches.InsertOnSubmit(b);
                context.SubmitChanges();
                return b.UId;
            }

        }

        public Guid UpdateBatch(Guid uid, string name, int number, DateTime date, string protocol)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                var p = context.Protocols.SingleOrDefault(x => x.Name == protocol.Trim());
                Batch b = context.Batches.Single(x => x.UId == uid);
                b.Name = name;
                b.Number = number;
                b.Date = date;
                b.Protocol_Fk = p != null ? p.UId : Guid.Empty;
                context.SubmitChanges();
                return b.UId;
            }

        }

        public void DeletBatch(Guid uid)
        {
            using (EnzymeDBDataContext context = GetDataContext())
            {
                Batch m = context.Batches.Single(x => x.UId == uid);
                context.Batches.DeleteOnSubmit(m);
                context.SubmitChanges();
            }
        }
    }
}
